Interpretable ML methods – DONE
We will once again use the lending club data that we used in the 3rd assignment. We will focus on the random forest model, which I recreate below. (Note we use this model even though the true negative rate of the training set is quite bad.)
set.seed(494) # for reproducibility
#split data
lending_split <- initial_split(lending_club,
prop = .75,
strata = Class)
lending_training <- training(lending_split)
lending_test <- testing(lending_split)
#create recipe - including up and downsampling for model fitting
set.seed(456)
rf_recipe <-
recipe(Class ~ .,
data = lending_training) %>%
step_upsample(Class, over_ratio = .5) %>%
step_downsample(Class, under_ratio = 1) %>%
step_mutate_at(all_numeric(),
fn = ~as.numeric(.))
# create model
rf_model <-
rand_forest(mtry = tune(),
min_n = tune(),
trees = 100) %>%
set_mode("classification") %>%
set_engine("ranger")
# create workflow
rf_workflow <-
workflow() %>%
add_recipe(rf_recipe) %>%
add_model(rf_model)
grid_regular(finalize(mtry(),
lending_training %>%
select(-Class)),
min_n(),
levels = 3)
# create penalty grid
rf_penalty_grid <-
grid_regular(finalize(mtry(),
lending_training %>%
select(-Class)),
min_n(),
levels = 3)
# create cv samples
set.seed(494) #for reproducible 5-fold
lending_cv <- vfold_cv(lending_training,
v = 5)
# tune model
rf_tune <-
rf_workflow %>%
tune_grid(
resamples = lending_cv,
grid = rf_penalty_grid
)
# find model with best accuracy
best_accuracy <-
rf_tune %>%
select_best(metric = "accuracy")
# finalize model
rf_final <- rf_workflow %>%
finalize_workflow(best_accuracy) %>%
fit(data = lending_training)
1. Use functions from the DALEX and DALEXtra libraries to create a histogram and boxplot of the residuals from the training data.
rf_explain <-
explain_tidymodels(
model = rf_final,
data = lending_training %>% select(-Class),
y = lending_training %>%
mutate(Class_num = as.integer(Class =="good")) %>%
pull(Class_num),
label = "rf"
)
## Preparation of a new explainer is initiated
## -> model label : rf
## -> data : 7392 rows 22 cols
## -> data : tibble converted into a data.frame
## -> target variable : 7392 values
## -> predict function : yhat.workflow will be used ( [33m default [39m )
## -> predicted values : No value for predict function target column. ( [33m default [39m )
## -> model_info : package tidymodels , ver. 0.1.3 , task classification ( [33m default [39m )
## -> predicted values : numerical, min = 0 , mean = 0.8477854 , max = 1
## -> residual function : difference between y and yhat ( [33m default [39m )
## -> residuals : numerical, min = -0.095 , mean = 0.1004018 , max = 0.72
## [32m A new explainer has been created! [39m
rf_mod_perf <- model_performance(rf_explain)
rf_mod_perf
## Measures for: classification
## recall : 0.9897275
## precision : 1
## f1 : 0.9948372
## accuracy : 0.9902597
## auc : 1
##
## Residuals:
## 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
## -0.095 0.000 0.010 0.020 0.040 0.060 0.080 0.120 0.180 0.270 0.720
hist_plot <-
plot(rf_mod_perf,
geom = "histogram")
box_plot <-
plot(rf_mod_perf,
geom = "boxplot")
hist_plot

box_plot

How do they look? Any interesting behavior?
They are not centered around 0 as you would want a good model to do. They appear to skew right heavily well into the positives indicating a continuous underprediction. This is very significant as it shows a pattern of underpredictions which shouldn’t be a constant pattern if it is a good model.
2. Use DALEX functions to create a variable importance plot from this model.
rf_var_imp <-
model_parts(
rf_explain
)
plot(rf_var_imp, show_boxplots = TRUE)

What are the most important variables?
The most importannt variables used to help correctly predict if a loan was paid back was interest rate, sub_grade, open_il_24m, and annual income. These are not surprising at all because most of them relate exactly back to affecting one’s ability to repay … ie if an interest rate is incredibly high it makes the pay back extremely less likely, if one has a very low income it makes payback very difficult….
3. Write a function called cp_profile to make a CP profile.
The function will take an explainer, a new observation, and a variable name as its arguments and
create a CP profile for a quantitative predictor variable.
You will need to use the predict_profile() function inside the function you create - put the variable name there so the plotting part is easier.
obs2 <- lending_training %>%
slice(2)
obs2
CODE TEST FOR ANNUAL INC AND INT RATE
is.integer(lending_training$annual_inc)
## [1] FALSE
is.integer(lending_training$int_rate)
## [1] FALSE
TEST 1
cp_profile_test <- predict_profile(explainer = rf_explain,
new_observation = obs2,
variables = c("annual_inc", "int_rate"))
cp_profile_test
cp_profile_test %>%
filter(`_vname_` %in% c("annual_inc")) %>%
ggplot(aes(x = annual_inc,
y = `_yhat_`)) +
geom_line()

With y_hat being our predicted class outcome (with 1 = good pay back on time and 0 = bad not paid back on time) it appears to show us: as income increases, while it doesn’t appear to be a major difference, it is slightly more likely the person is not on time w repayment. Yet, it appears it is much more constant w the trend line for higher income whereas lower income is much more chaotic in predicted payback.
TEST 2
cp_profile_test %>%
filter(`_vname_` %in% c("int_rate")) %>%
ggplot(aes(x = int_rate,
y = `_yhat_`)) +
geom_line()

With y_hat being our predicted class outcome (with 1 = good pay back on time and 0 = bad not paid back on time) it appears to show us a very obvious trend. As expected, as the interest rate increases meaning increasingly more money is owed back than borrowed, the ability to pay on time and the predicted class value races downward closer to bad meaning the payment is much less likely to be on time.
Now Function Writing time…
Write a function called cp_profile to make a CP profile.
The function will take an explainer, a new observation, and a variable name as its arguments and
create a CP profile for a quantitative predictor variable.
You will need to use the predict_profile() function inside the function you create - put the variable name there so the plotting part is easier.
You’ll also want to use .data[[]] rather than aes() and quote the variables. Use the cp_profile() function to create one CP profile of your choosing.
Be sure to choose a variable that is numeric, not integer. There seem to be issues with those that I’m looking into.
cp_profile <- function(explainer_fn, new_obs, `var`) {
profile <- predict_profile(explainer = explainer_fn,
new_observation = new_obs,
variables = `var`)
graph <- profile %>%
filter(`_vname_` %in% c(`var`)) %>%
ggplot(aes(x = .data[[`var`]], y = `_yhat_`)) +
geom_line()
graph
}
lending_numeric <- select_if(lending_training, is.numeric)
lending_numeric
cp_profile(rf_explain, obs2, "annual_inc")

cp_profile(rf_explain, obs2, "int_rate")

cp_profile(rf_explain, obs2, "funded_amnt")

cp_profile(rf_explain, obs2, "revol_util")

4. Use DALEX functions to create partial dependence plots (with the CP profiles in gray) for the 3-4 most important variables.
If the important variables are categorical, you can instead make a CP profile for 3 observations in the dataset and discuss how you could go about constructing a partial dependence plot for a categorical variable (you don’t have to code it, but you can if you want an extra challenge).
Most Important: interest rate, sub_grade, open_il_24m, and annual income
is.factor(lending_training$int_rate)
## [1] FALSE
is.factor(lending_training$sub_grade)
## [1] TRUE
is.factor(lending_training$open_il_24m)
## [1] FALSE
is.factor(lending_training$annual_inc)
## [1] FALSE
rf_pdp <- model_profile(explainer = rf_explain,
variables = c("int_rate", "open_il_24m", "annual_inc"))
plot(rf_pdp,
variables = c("int_rate", "open_il_24m", "annual_inc"),
geom = "profiles")

Discuss how you could go about constructing a partial dependence plot for a categorical variable
– Subgrade = Categorical
– Steps to Create PDP for subgrade
++++ one major step to do and then can use code above: convert from categorical / factor to numeric value !!!
– for each level of subgrade, working from top to bottom, assign a numeric value using a series of ifelse statements within mutate for subgrade within lending training
– now, w subgrade being numeric, can feed right into the list of variables above…
5. Choose 3 observations and do the following for each observation:
OBSERVATION 2
obs2_q5 <- lending_test %>%
slice(2)
obs2_q5
- Construct a break-down plot using the default ordering.
pp_rf_2 <- predict_parts(explainer = rf_explain,
new_observation = obs2_q5,
type = "break_down")
plot(pp_rf_2)

Interpret the resulting graph. Which variables contribute most to each observation’s prediction?
First we can seethe initial bars start at the intercept of 0.848 which marks the value of the average predicted class (1 = good, 0 = bad) when applying the rf model to the training data.
We can see we end up 0.108 lower in our predicted class of paying back a loan than the intercept thanks to some heavy hitting negative variables. The resulting 0.74 is a fairly strong good prediction.
We can see that the largest and most influential tugs from variables down towards the actual predicted value from the intercept comes from:
total_bal_il == 55445 –> -0.048
num_il_tl == 10 –> -0.032
open_il_24m == 2 –> -0.029
This means that for each of these variables, if they were fixed at the values they are set to, the change in average class prediction would be these large negative values.
- Construct a SHAP graph and interpret it.
rf_shap_2 <-predict_parts(explainer = rf_explain,
new_observation = obs2_q5,
type = "shap",
B = 20
)
plot(rf_shap_2)

Does it tell a similar story to the break-down plot?
We can see that SEVERAL variables have boxplots of their effects straddling zero spreading across both positive and negative values.
These variables set to their appropriate constants include:
total_bal_il
int_rate
sub_grade
num_il_tl
emp_length
total_il_high_credit_limit
This means that when we changed the order of considering these variables and re-run the breakdown test 20 times, we have the variables having multiple instances of both negative and positive effects.
This narrative is in direct conflict with the clear cut picture we were painted above about variable effects.
- Construct a LIME graph (follow my code carefully).
model_type.dalex_explainer <- DALEXtra::model_type.dalex_explainer
predict_model.dalex_explainer <- DALEXtra::predict_model.dalex_explainer
set.seed(2)
lime_rf_2 <- predict_surrogate(explainer = rf_explain,
new_observation = obs2_q5 %>%
select(-Class),
n_features = 5,
n_permutations = 1000,
type = "lime")
lime_rf_2 %>%
select(model_r2, model_prediction, prediction) %>%
distinct()
plot(lime_rf_2) +
labs(x = "Variable")

How close is each original prediction to the prediction from the local model? Interpret the result. You can also try using fewer or more variables in the local model than I used in the example.
Prediction from Original RF Model: 0.74
Prediction from Local Model: 0.82 (+0.08)
The predictions are pretty close…the Local Model is +0.08…
– They are basically the same because most variables have roughly the same impact… probably because the total_il_high_credit_limit variable appears to have slightly greater impact here and some variables impacts may be on the higher end of their boxplots…also just so happens 3 / 5 vars have positive impacts of all same size….
Local Model R^2: 0.05 – Not Good
Bar Plot Shows Variable Importance From Local Model:
– annual income nd total bal il have big negative effects
– the rest appear to have equally as high impact vars but positive
OBSERVATION 20
obs20 <- lending_test %>%
slice(20)
obs20
- Construct a break-down plot using the default ordering.
pp_rf_20 <- predict_parts(explainer = rf_explain,
new_observation = obs20,
type = "break_down")
plot(pp_rf_20)

Interpret the resulting graph. Which variables contribute most to each observation’s prediction?
First we can see the initial bars start at the intercept of 0.848 which marks the value of the average predicted class (1 = good, 0 = bad) when applying the rf model to the training data.
We can see we end up 0.018 lower in our predicted class of paying back a loan than the intercept. This very little change is due to the fact that a lot of the variables’ effects have cancelling effects (fairly even split of + and - effects). The resulting 0.83 is a strong good prediction.
We can see that the largest tugs come from variables both + and - (and I guess that makes them most influential as they cancel each other out for the most part)…:
int_rate == 12.99 –> -0.04
total_bal_il == 26275 –> 0.037
inq_fi == 4 –> -0.028
This means that for each of these variables, if they were fixed at the values they are set to, the change in average class prediction would be their outputted vals.
This was a weird one to eval contribution: 1) do the bigger tugs still count as most significant because they are cancelling eachother out or 2) do the small negative tugs count more even though they wouldn’t matter if the big tugs didn’t cancel out????
- Construct a SHAP graph and interpret it.
rf_shap_20 <-predict_parts(explainer = rf_explain,
new_observation = obs20,
type = "shap",
B = 20
)
plot(rf_shap_20)

Does it tell a similar story to the break-down plot?
We can see that SEVERAL variables have boxplots of their effects straddling zero spreading across both positive and negative values.
These variables set to their appropriate constants include:
total_bal_il
sub_grade
annual_inc
int_rate
addr_state
all_util
inq_last_6mths
This means that when we changed the order of considering these variables and re-run the breakdown test 20 times, we have almost every single variable having multiple instances of both negative and positive effects.
This narrative is in direct conflict with the clear cut picture we were painted above as the vars effects are not as clear as we thought above.
- Construct a LIME graph (follow my code carefully).
model_type.dalex_explainer <- DALEXtra::model_type.dalex_explainer
predict_model.dalex_explainer <- DALEXtra::predict_model.dalex_explainer
set.seed(2)
lime_rf_20 <- predict_surrogate(explainer = rf_explain,
new_observation = obs20 %>%
select(-Class),
n_features = 5,
n_permutations = 1000,
type = "lime")
lime_rf_20 %>%
select(model_r2, model_prediction, prediction) %>%
distinct()
plot(lime_rf_20) +
labs(x = "Variable")

How close is each original prediction to the prediction from the local model? Interpret the result. You can also try using fewer or more variables in the local model than I used in the example.
Prediction from Original RF Model: 0.83
Prediction from Local Model: 0.8 (-0.03)
The predictions are fairly close…the Local Model is -0.03…
– This likely is because of the massive negative impact of revol_util and annual_inc which pushed the pred slightly below as they countered the pos impact from others. Annual Income’s effects appear to be much more negative here than above…
Local Model R^2: 0.07 – Terrible
Bar Plot Shows Variable Importance From Local Model:
– revol_util has a massive negative impact
– annual income has equally massive negative impact
– inq last 6 mths and total il high credit have moderately large positive impacts
OBSERVATION 200
obs200 <- lending_test %>%
slice(200)
obs200
- Construct a break-down plot using the default ordering.
pp_rf_200 <- predict_parts(explainer = rf_explain,
new_observation = obs200,
type = "break_down")
plot(pp_rf_200)

Interpret the resulting graph. Which variables contribute most to each observation’s prediction?
First we can see the initial bars start at the intercept of 0.848 which marks the value of the average predicted class (1 = good, 0 = bad) when applying the rf model to the training data.
We can see we end up 0.132 HIGHER in our predicted class of paying back a loan than the intercept thanks to some big positive jumps from influential variables. The resulting 0.98 is an extremely strong good prediction.
We can see that the largest positive jumps from variables come from:
int_rate == 13.44 –> 0.029
sub_grade == 13 –> 0.029
total_bal_il == 0 –> 0.028
and also it helps that all but 3 in this case have positive impacts…
This means that for each of these variables, if they were fixed at the values they are set to, the change in average class prediction would be these large positive values which pulled our prediction for this observation up very high.
- Construct a SHAP graph and interpret it.
rf_shap_200 <-predict_parts(explainer = rf_explain,
new_observation = obs200,
type = "shap",
B = 20
)
plot(rf_shap_200)

Does it tell a similar story to the break-down plot?
We can see that SEVERAL variables have boxplots of their effects straddling zero spreading across both positive and negative values.
These variables set to their appropriate constants include:
total_bal_il
sub_grade
int_rate
total_il_high_credit_limit
annual_inc
num_il_til
This means that when we changed the order of considering these variables and re-run the breakdown test 20 times, we have these variables having multiple instances of both negative and positive effects.
This narrative is in direct conflict with the clear cut picture we were painted above as we see the impacts of vars are not as obvious.
- Construct a LIME graph (follow my code carefully).
model_type.dalex_explainer <- DALEXtra::model_type.dalex_explainer
predict_model.dalex_explainer <- DALEXtra::predict_model.dalex_explainer
set.seed(2)
lime_rf_200 <- predict_surrogate(explainer = rf_explain,
new_observation = obs200 %>%
select(-Class),
n_features = 5,
n_permutations = 1000,
type = "lime")
lime_rf_200 %>%
select(model_r2, model_prediction, prediction) %>%
distinct()
plot(lime_rf_200) +
labs(x = "Variable")

How close is each original prediction to the prediction from the local model? Interpret the result. You can also try using fewer or more variables in the local model than I used in the example.
Prediction from Original RF Model: 0.98
Prediction from Local Model: 0.81 (-0.17)
The predictions are not close…the Local Model is -0.17…
– This likely is because here we have massive negative impacts seen from revol_util and annual_income, much higher than displayed above, which account for the massive drop in predicted class score.
Local Model R^2: 0.06 – Awful
Bar Plot Shows Variable Importance From Local Model:
– revol_util, annual income have massively large negative impacts that outpace the positive impacts of inq last 6mths, inq_fi, and total_bal_il.
6. Describe how you would use the interpretable machine learning tools we’ve learned (both local and global) in future machine learning projects? How does each of them help you?
I could find great use in using the boxplot and histogram of residuals for a model from global interpretable ML and from local interpretable ML I would definitely have great value from using breakdown profiles and shapley plots.
For Global: Visualizing Residuals
This past Summer, I had an internship with a Fintech company called AvidXChange, a Charlotte, NC based Accounts Payable Automation company. I used sql to pull data from databases and machine learning skills to use neural networks and deep learning. We wanted to more accurately predicted which of our clients accepted Virtual Credit Card and which didn’t as this was a big problem. Avid makes most of their money from fees on VCC cards being used to pay off invoices as opposed to just direct deposit (this is more ideal for most as they get the money much quicker despite a very small fee). If able to better identify who simply can’t take VCC, it would save a lot of time and money better used on other clients.
When we finalized the mode, I would have loved to see how well it predicted through seeing the distribution of its residuals. My model also was a class prediction like w the lending data, specifically using the spelling and make up of the company name to predict if they took VCC (the hunch was that companies that appeared to be people did not accept VCC and the model used a series of dictionaries for letters and names from census data to distinguish names).
If I could analyze the residuals a bit more with things like the boxplot and histogram like we did above, I would have been able to see a bit better how well we predicted correctly. Specially, I would have been able to see if it was a good model beyond simply looking at output metrics like I did. If I could have visualized the residuals and seen how it tended to predict the majority class more than most other things, that would have been very easy to see and cool to help show the results as opposed to simply telling people the results with metrics filled with jargon.
For Local: Breakdown Profiles and Shapley
Also, in my internship I added on to a VCC model a propensity score value which predicted a probability at which a company (we thought) would accept VCC. It took in many variables including past invoices, size of the invoice, size of the company, if they have taken VCC in the past before, location…
I would have loved to see for specific companies which variables were more significant in driving the propensity scores to where they went. Obviously Panera takes VCC because they take credit card but what variable specifically was honing in on this?
Seeing the little tug of war between variables and how they effected the outcome woud have been really cool. Additionally, I would have liked to see the permanence of these variables effects beyond just one observation. If I could have also used a shap plot, I could have seen how firm these variable impacts were as well and seen how it changed from large to small company occurrences.
7. Save this final model using the write_rds() function - see the section of the tidymodels intro for a similar example, but we’re using write_rds() instead of saveRDS(). We are going to use the model in the next part. You’ll want to save it in the folder where you create your shiny app. Run the code, and then add eval=FALSE to the code chunk options (next to the r inside the curly brackets) so it doesn’t rerun this each time you knit.
# # finalize model
# rf_final <- rf_workflow %>%
# finalize_workflow(best_accuracy) %>%
# fit(data = lending_training)
write_rds(rf_final, "rf_final.rds")
rf_final_read <- readRDS("rf_final.rds")
LS0tCnRpdGxlOiAnQXNzaWdubWVudCAjNScKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpCmBgYAoKYGBge3IgbGlicmFyaWVzfQojIFNFRSBtb2RlbGRhdGEgcGFja2FnZSBmb3IgbmV3IGRhdGFzZXRzCmxpYnJhcnkodGlkeXZlcnNlKSAgICAgICAgICMgZm9yIGdyYXBoaW5nIGFuZCBkYXRhIGNsZWFuaW5nCmxpYnJhcnkodGlkeW1vZGVscykgICAgICAgICMgZm9yIG1vZGVsaW5nCmxpYnJhcnkocmFuZ2VyKSAgICAgICAgICAgICMgZm9yIHJhbmRvbSBmb3Jlc3QgLSB3aWxsIG5lZWQgZm9yIHNoaW55IGFwcApsaWJyYXJ5KGx1YnJpZGF0ZSkgICAgICAgICAjIGZvciBkYXRlIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KHRoZW1pcykgICAgICAgICAgICAjIGZvciB1cCBhbmQgZG93bnNhbXBsaW5nCmxpYnJhcnkoREFMRVgpICAgICAgICAgICAgICMgZm9yIG1vZGVsIGludGVycHJldGF0aW9uICAKbGlicmFyeShEQUxFWHRyYSkgICAgICAgICAgIyBmb3IgZXh0ZW5zaW9uIG9mIERBTEVYCmxpYnJhcnkobGltZSkKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkgIyBMaXNhJ3MgZmF2b3JpdGUgdGhlbWUKYGBgCgpgYGB7ciBkYXRhfQpkYXRhKCJsZW5kaW5nX2NsdWIiKQojIERhdGEgZGljdGlvbmFyeSAoYXMgY2xvc2UgYXMgSSBjb3VsZCBmaW5kKTogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS93b3Jkc2ZvcnRoZXdpc2UvbGVuZGluZy1jbHViL2Rpc2N1c3Npb24vMTcwNjkxCmBgYAoKCiMjIFB1dCBpdCBvbiBHaXRIdWIhIC0tIERPTkUgICAgICAgIAoKCkdpdGh1YiBSZXBvIExpbms6IGh0dHBzOi8vZ2l0aHViLmNvbS9hcGFsbWExMjcvYXNzaWdubWVudC01CgoKIyMgSW50ZXJwcmV0YWJsZSBNTCBtZXRob2RzIC0tIERPTkUKCldlIHdpbGwgb25jZSBhZ2FpbiB1c2UgdGhlIGxlbmRpbmcgY2x1YiBkYXRhIHRoYXQgd2UgdXNlZCBpbiB0aGUgM3JkIGFzc2lnbm1lbnQuIFdlIHdpbGwgZm9jdXMgb24gdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwsIHdoaWNoIEkgcmVjcmVhdGUgYmVsb3cuIChOb3RlIHdlIHVzZSB0aGlzIG1vZGVsIGV2ZW4gdGhvdWdoIHRoZSB0cnVlIG5lZ2F0aXZlIHJhdGUgb2YgdGhlIHRyYWluaW5nIHNldCBpcyBxdWl0ZSBiYWQuKQoKYGBge3J9CnNldC5zZWVkKDQ5NCkgIyBmb3IgcmVwcm9kdWNpYmlsaXR5Cgojc3BsaXQgZGF0YQpsZW5kaW5nX3NwbGl0IDwtIGluaXRpYWxfc3BsaXQobGVuZGluZ19jbHViLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcCA9IC43NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmF0YSA9IENsYXNzKQoKbGVuZGluZ190cmFpbmluZyA8LSB0cmFpbmluZyhsZW5kaW5nX3NwbGl0KQpsZW5kaW5nX3Rlc3QgPC0gdGVzdGluZyhsZW5kaW5nX3NwbGl0KQoKCiNjcmVhdGUgcmVjaXBlIC0gaW5jbHVkaW5nIHVwIGFuZCBkb3duc2FtcGxpbmcgZm9yIG1vZGVsIGZpdHRpbmcKc2V0LnNlZWQoNDU2KQpyZl9yZWNpcGUgPC0gCiAgcmVjaXBlKENsYXNzIH4gLiwKICAgICAgICAgZGF0YSA9IGxlbmRpbmdfdHJhaW5pbmcpICU+JSAKICBzdGVwX3Vwc2FtcGxlKENsYXNzLCBvdmVyX3JhdGlvID0gLjUpICU+JSAKICBzdGVwX2Rvd25zYW1wbGUoQ2xhc3MsIHVuZGVyX3JhdGlvID0gMSkgJT4lIAogIHN0ZXBfbXV0YXRlX2F0KGFsbF9udW1lcmljKCksIAogICAgICAgICAgICAgICAgIGZuID0gfmFzLm51bWVyaWMoLikpCgojIGNyZWF0ZSBtb2RlbApyZl9tb2RlbCA8LSAKICByYW5kX2ZvcmVzdChtdHJ5ID0gdHVuZSgpLCAKICAgICAgICAgICAgICBtaW5fbiA9IHR1bmUoKSwgCiAgICAgICAgICAgICAgdHJlZXMgPSAxMDApICU+JSAKICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKSAlPiUgCiAgc2V0X2VuZ2luZSgicmFuZ2VyIikKCiMgY3JlYXRlIHdvcmtmbG93CnJmX3dvcmtmbG93IDwtCiAgd29ya2Zsb3coKSAlPiUgCiAgYWRkX3JlY2lwZShyZl9yZWNpcGUpICU+JSAKICBhZGRfbW9kZWwocmZfbW9kZWwpCgogIGdyaWRfcmVndWxhcihmaW5hbGl6ZShtdHJ5KCksCiAgICAgICAgICAgICAgICAgICAgICAgIGxlbmRpbmdfdHJhaW5pbmcgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1DbGFzcykpLAogICAgICAgICAgICAgICBtaW5fbigpLAogICAgICAgICAgICAgICBsZXZlbHMgPSAzKQoKIyBjcmVhdGUgcGVuYWx0eSBncmlkCiAgcmZfcGVuYWx0eV9ncmlkIDwtIApncmlkX3JlZ3VsYXIoZmluYWxpemUobXRyeSgpLAogICAgICAgICAgICAgICAgICAgICAgICBsZW5kaW5nX3RyYWluaW5nICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtQ2xhc3MpKSwKICAgICAgICAgICAgICAgbWluX24oKSwKICAgICAgICAgICAgICAgbGV2ZWxzID0gMykKCgojIGNyZWF0ZSBjdiBzYW1wbGVzCnNldC5zZWVkKDQ5NCkgI2ZvciByZXByb2R1Y2libGUgNS1mb2xkCmxlbmRpbmdfY3YgPC0gdmZvbGRfY3YobGVuZGluZ190cmFpbmluZywKICAgICAgICAgICAgICAgICAgICAgICB2ID0gNSkKCiMgdHVuZSBtb2RlbApyZl90dW5lIDwtIAogIHJmX3dvcmtmbG93ICU+JSAKICB0dW5lX2dyaWQoCiAgICByZXNhbXBsZXMgPSBsZW5kaW5nX2N2LAogICAgZ3JpZCA9IHJmX3BlbmFsdHlfZ3JpZAogICkKCiMgZmluZCBtb2RlbCB3aXRoIGJlc3QgYWNjdXJhY3kKYmVzdF9hY2N1cmFjeSA8LQogIHJmX3R1bmUgJT4lIAogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpCgojIGZpbmFsaXplIG1vZGVsCnJmX2ZpbmFsIDwtIHJmX3dvcmtmbG93ICU+JSAKICBmaW5hbGl6ZV93b3JrZmxvdyhiZXN0X2FjY3VyYWN5KSAlPiUgCiAgZml0KGRhdGEgPSBsZW5kaW5nX3RyYWluaW5nKQpgYGAKCioqMS4gVXNlIGZ1bmN0aW9ucyBmcm9tIHRoZSBgREFMRVhgIGFuZCBgREFMRVh0cmFgIGxpYnJhcmllcyB0byBjcmVhdGUgYSBoaXN0b2dyYW0gYW5kIGJveHBsb3Qgb2YgdGhlIHJlc2lkdWFscyBmcm9tIHRoZSB0cmFpbmluZyBkYXRhLioqIAoKCmBgYHtyfQpyZl9leHBsYWluIDwtIAogIGV4cGxhaW5fdGlkeW1vZGVscygKICAgIG1vZGVsID0gcmZfZmluYWwsCiAgICBkYXRhID0gbGVuZGluZ190cmFpbmluZyAlPiUgc2VsZWN0KC1DbGFzcyksIAogICAgeSA9IGxlbmRpbmdfdHJhaW5pbmcgJT4lIAogICAgICBtdXRhdGUoQ2xhc3NfbnVtID0gYXMuaW50ZWdlcihDbGFzcyA9PSJnb29kIikpICU+JSAKICAgICAgcHVsbChDbGFzc19udW0pLAogICAgbGFiZWwgPSAicmYiCiAgKQoKYGBgCgoKYGBge3J9CnJmX21vZF9wZXJmIDwtICBtb2RlbF9wZXJmb3JtYW5jZShyZl9leHBsYWluKQoKcmZfbW9kX3BlcmYKYGBgCgoKCgpgYGB7cn0KaGlzdF9wbG90IDwtIAogIHBsb3QocmZfbW9kX3BlcmYsCiAgICAgICBnZW9tID0gImhpc3RvZ3JhbSIpCgoKYm94X3Bsb3QgPC0KICBwbG90KHJmX21vZF9wZXJmLAogICAgICAgZ2VvbSA9ICJib3hwbG90IikKCmBgYAoKCmBgYHtyfQpoaXN0X3Bsb3QKYGBgCgpgYGB7cn0KYm94X3Bsb3QKYGBgCgoKCioqSG93IGRvIHRoZXkgbG9vaz8gQW55IGludGVyZXN0aW5nIGJlaGF2aW9yPyoqCgpUaGV5IGFyZSBub3QgY2VudGVyZWQgYXJvdW5kIDAgYXMgeW91IHdvdWxkIHdhbnQgYSBnb29kIG1vZGVsIHRvIGRvLiBUaGV5IGFwcGVhciB0byBza2V3IHJpZ2h0IGhlYXZpbHkgd2VsbCBpbnRvIHRoZSBwb3NpdGl2ZXMgaW5kaWNhdGluZyBhIGNvbnRpbnVvdXMgdW5kZXJwcmVkaWN0aW9uLiAgVGhpcyBpcyB2ZXJ5IHNpZ25pZmljYW50IGFzIGl0IHNob3dzIGEgcGF0dGVybiBvZiB1bmRlcnByZWRpY3Rpb25zIHdoaWNoIHNob3VsZG4ndCBiZSBhIGNvbnN0YW50IHBhdHRlcm4gaWYgaXQgaXMgYSBnb29kIG1vZGVsLiAKCgoqKjIuIFVzZSBgREFMRVhgIGZ1bmN0aW9ucyB0byBjcmVhdGUgYSB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QgZnJvbSB0aGlzIG1vZGVsLioqIAoKYGBge3J9CnJmX3Zhcl9pbXAgPC0gCiAgbW9kZWxfcGFydHMoCiAgICByZl9leHBsYWluCiAgICApCgpwbG90KHJmX3Zhcl9pbXAsIHNob3dfYm94cGxvdHMgPSBUUlVFKQpgYGAKCgoKKipXaGF0IGFyZSB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzPyoqCgpUaGUgbW9zdCBpbXBvcnRhbm50IHZhcmlhYmxlcyB1c2VkIHRvIGhlbHAgY29ycmVjdGx5IHByZWRpY3QgaWYgYSBsb2FuIHdhcyBwYWlkIGJhY2sgd2FzICoqaW50ZXJlc3QgcmF0ZSoqLCAqKnN1Yl9ncmFkZSoqLCAqKm9wZW5faWxfMjRtKiosIGFuZCAqKmFubnVhbCBpbmNvbWUqKi4gIFRoZXNlIGFyZSBub3Qgc3VycHJpc2luZyBhdCBhbGwgYmVjYXVzZSBtb3N0IG9mIHRoZW0gcmVsYXRlIGV4YWN0bHkgYmFjayB0byBhZmZlY3Rpbmcgb25lJ3MgYWJpbGl0eSB0byByZXBheSAuLi4gaWUgaWYgYW4gaW50ZXJlc3QgcmF0ZSBpcyBpbmNyZWRpYmx5IGhpZ2ggaXQgbWFrZXMgdGhlIHBheSBiYWNrIGV4dHJlbWVseSBsZXNzIGxpa2VseSwgaWYgb25lIGhhcyBhIHZlcnkgbG93IGluY29tZSBpdCBtYWtlcyBwYXliYWNrIHZlcnkgZGlmZmljdWx0Li4uLgoKCioqMy4gV3JpdGUgYSBmdW5jdGlvbiBjYWxsZWQgYGNwX3Byb2ZpbGVgIHRvIG1ha2UgYSBDUCBwcm9maWxlLioqIAoKVGhlIGZ1bmN0aW9uIHdpbGwgdGFrZSBhbiBleHBsYWluZXIsIAphIG5ldyBvYnNlcnZhdGlvbiwgCmFuZCBhIHZhcmlhYmxlIG5hbWUgYXMgaXRzIGFyZ3VtZW50cyBhbmQgCgpjcmVhdGUgYSBDUCBwcm9maWxlIGZvciBhIHF1YW50aXRhdGl2ZSBwcmVkaWN0b3IgdmFyaWFibGUuIAoKCllvdSB3aWxsIG5lZWQgdG8gdXNlIHRoZSBgcHJlZGljdF9wcm9maWxlKClgIGZ1bmN0aW9uIGluc2lkZSB0aGUgZnVuY3Rpb24geW91IGNyZWF0ZSAtIApwdXQgdGhlIHZhcmlhYmxlIG5hbWUgdGhlcmUgc28gdGhlIHBsb3R0aW5nIHBhcnQgaXMgZWFzaWVyLgoKCmBgYHtyfQpvYnMyIDwtIGxlbmRpbmdfdHJhaW5pbmcgJT4lIAogIHNsaWNlKDIpCm9iczIKYGBgCgoqKkNPREUgVEVTVCBGT1IgQU5OVUFMIElOQyBBTkQgSU5UIFJBVEUqKgoKYGBge3J9CmlzLmludGVnZXIobGVuZGluZ190cmFpbmluZyRhbm51YWxfaW5jKQppcy5pbnRlZ2VyKGxlbmRpbmdfdHJhaW5pbmckaW50X3JhdGUpCgpgYGAKCgoqKlRFU1QgMSoqCgoKYGBge3J9CmNwX3Byb2ZpbGVfdGVzdCA8LSBwcmVkaWN0X3Byb2ZpbGUoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3X29ic2VydmF0aW9uID0gb2JzMiwKICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJhbm51YWxfaW5jIiwgImludF9yYXRlIikpCgoKY3BfcHJvZmlsZV90ZXN0CmBgYAoKYGBge3J9CmNwX3Byb2ZpbGVfdGVzdCAlPiUgCiAgZmlsdGVyKGBfdm5hbWVfYCAlaW4lIGMoImFubnVhbF9pbmMiKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGFubnVhbF9pbmMsCiAgICAgICAgICAgICB5ID0gYF95aGF0X2ApKSArCiAgZ2VvbV9saW5lKCkgCmBgYAoKKipXaXRoIHlfaGF0IGJlaW5nIG91ciBwcmVkaWN0ZWQgY2xhc3Mgb3V0Y29tZSAod2l0aCAxID0gZ29vZCBwYXkgYmFjayBvbiB0aW1lIGFuZCAwID0gYmFkIG5vdCBwYWlkIGJhY2sgb24gdGltZSkgaXQgYXBwZWFycyB0byBzaG93IHVzOiBhcyBpbmNvbWUgaW5jcmVhc2VzLCB3aGlsZSBpdCBkb2Vzbid0IGFwcGVhciB0byBiZSBhIG1ham9yIGRpZmZlcmVuY2UsIGl0IGlzIHNsaWdodGx5IG1vcmUgbGlrZWx5IHRoZSBwZXJzb24gaXMgbm90IG9uIHRpbWUgdyByZXBheW1lbnQuICBZZXQsIGl0IGFwcGVhcnMgaXQgaXMgbXVjaCBtb3JlIGNvbnN0YW50IHcgdGhlIHRyZW5kIGxpbmUgZm9yIGhpZ2hlciBpbmNvbWUgd2hlcmVhcyBsb3dlciBpbmNvbWUgaXMgbXVjaCBtb3JlIGNoYW90aWMgaW4gcHJlZGljdGVkIHBheWJhY2suKiogCgoKKipURVNUIDIqKgoKCmBgYHtyfQpjcF9wcm9maWxlX3Rlc3QgJT4lIAogIGZpbHRlcihgX3ZuYW1lX2AgJWluJSBjKCJpbnRfcmF0ZSIpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gaW50X3JhdGUsCiAgICAgICAgICAgICB5ID0gYF95aGF0X2ApKSArCiAgZ2VvbV9saW5lKCkgCmBgYAoKKipXaXRoIHlfaGF0IGJlaW5nIG91ciBwcmVkaWN0ZWQgY2xhc3Mgb3V0Y29tZSAod2l0aCAxID0gZ29vZCBwYXkgYmFjayBvbiB0aW1lIGFuZCAwID0gYmFkIG5vdCBwYWlkIGJhY2sgb24gdGltZSkgaXQgYXBwZWFycyB0byBzaG93IHVzIGEgdmVyeSBvYnZpb3VzIHRyZW5kLiAgQXMgZXhwZWN0ZWQsIGFzIHRoZSBpbnRlcmVzdCByYXRlIGluY3JlYXNlcyBtZWFuaW5nIGluY3JlYXNpbmdseSBtb3JlIG1vbmV5IGlzIG93ZWQgYmFjayB0aGFuIGJvcnJvd2VkLCB0aGUgYWJpbGl0eSB0byBwYXkgb24gdGltZSBhbmQgdGhlIHByZWRpY3RlZCBjbGFzcyB2YWx1ZSByYWNlcyBkb3dud2FyZCBjbG9zZXIgdG8gYmFkIG1lYW5pbmcgdGhlIHBheW1lbnQgaXMgbXVjaCBsZXNzIGxpa2VseSB0byBiZSBvbiB0aW1lLioqIAoKCgoqKk5vdyBGdW5jdGlvbiBXcml0aW5nIHRpbWUuLi4qKiAKCldyaXRlIGEgZnVuY3Rpb24gY2FsbGVkIGBjcF9wcm9maWxlYCB0byBtYWtlIGEgQ1AgcHJvZmlsZS4gCgpUaGUgZnVuY3Rpb24gd2lsbCB0YWtlIGFuIGV4cGxhaW5lciwgCmEgbmV3IG9ic2VydmF0aW9uLCAKYW5kIGEgdmFyaWFibGUgbmFtZSBhcyBpdHMgYXJndW1lbnRzIGFuZCAKCmNyZWF0ZSBhIENQIHByb2ZpbGUgZm9yIGEgcXVhbnRpdGF0aXZlIHByZWRpY3RvciB2YXJpYWJsZS4gCgoKWW91IHdpbGwgbmVlZCB0byB1c2UgdGhlIGBwcmVkaWN0X3Byb2ZpbGUoKWAgZnVuY3Rpb24gaW5zaWRlIHRoZSBmdW5jdGlvbiB5b3UgY3JlYXRlIC0gCnB1dCB0aGUgdmFyaWFibGUgbmFtZSB0aGVyZSBzbyB0aGUgcGxvdHRpbmcgcGFydCBpcyBlYXNpZXIuCgoKWW91J2xsIGFsc28gd2FudCB0byB1c2UgYC5kYXRhW1tdXWAgcmF0aGVyIHRoYW4gYGFlcygpYCBhbmQgcXVvdGUgdGhlIHZhcmlhYmxlcy4gVXNlIHRoZSBgY3BfcHJvZmlsZSgpYCBmdW5jdGlvbiB0byBjcmVhdGUgb25lIENQIHByb2ZpbGUgb2YgeW91ciBjaG9vc2luZy4gCgpCZSBzdXJlIHRvIGNob29zZSBhIHZhcmlhYmxlIHRoYXQgaXMgbnVtZXJpYywgbm90IGludGVnZXIuIFRoZXJlIHNlZW0gdG8gYmUgaXNzdWVzIHdpdGggdGhvc2UgdGhhdCBJJ20gbG9va2luZyBpbnRvLgoKYGBge3J9CiBjcF9wcm9maWxlIDwtIGZ1bmN0aW9uKGV4cGxhaW5lcl9mbiwgbmV3X29icywgYHZhcmApIHsKICAKICAKICBwcm9maWxlIDwtIHByZWRpY3RfcHJvZmlsZShleHBsYWluZXIgPSBleHBsYWluZXJfZm4sIAogICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG5ld19vYnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGVzID0gYHZhcmApCiAgCiAgZ3JhcGggPC0gcHJvZmlsZSAlPiUgCiAgICBmaWx0ZXIoYF92bmFtZV9gICVpbiUgYyhgdmFyYCkpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IC5kYXRhW1tgdmFyYF1dLCB5ID0gYF95aGF0X2ApKSArCiAgICBnZW9tX2xpbmUoKSAKCiAgCiAgZ3JhcGgKIH0KCmBgYAoKCmBgYHtyfQpsZW5kaW5nX251bWVyaWMgPC0gc2VsZWN0X2lmKGxlbmRpbmdfdHJhaW5pbmcsIGlzLm51bWVyaWMpICAgICAgICAgICAgCgpsZW5kaW5nX251bWVyaWMKYGBgCgpgYGB7cn0KY3BfcHJvZmlsZShyZl9leHBsYWluLCBvYnMyLCAiYW5udWFsX2luYyIpCmBgYAoKYGBge3J9CmNwX3Byb2ZpbGUocmZfZXhwbGFpbiwgb2JzMiwgImludF9yYXRlIikKYGBgCgpgYGB7cn0KY3BfcHJvZmlsZShyZl9leHBsYWluLCBvYnMyLCAiZnVuZGVkX2FtbnQiKQoKYGBgCgpgYGB7cn0KY3BfcHJvZmlsZShyZl9leHBsYWluLCBvYnMyLCAicmV2b2xfdXRpbCIpCgpgYGAKCgoqKjQuIFVzZSBgREFMRVhgIGZ1bmN0aW9ucyB0byBjcmVhdGUgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3RzICh3aXRoIHRoZSBDUCBwcm9maWxlcyBpbiBncmF5KSBmb3IgdGhlIDMtNCBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXMuKiogCgoKSWYgdGhlIGltcG9ydGFudCB2YXJpYWJsZXMgYXJlIGNhdGVnb3JpY2FsLCB5b3UgY2FuIGluc3RlYWQgbWFrZSBhIENQIHByb2ZpbGUgZm9yIDMgb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhc2V0IGFuZCBkaXNjdXNzIGhvdyB5b3UgY291bGQgZ28gYWJvdXQgY29uc3RydWN0aW5nIGEgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3QgZm9yIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgKHlvdSBkb24ndCBoYXZlIHRvIGNvZGUgaXQsIGJ1dCB5b3UgY2FuIGlmIHlvdSB3YW50IGFuIGV4dHJhIGNoYWxsZW5nZSkuCgpNb3N0IEltcG9ydGFudDogICoqaW50ZXJlc3QgcmF0ZSoqLCAqKnN1Yl9ncmFkZSoqLCAqKm9wZW5faWxfMjRtKiosIGFuZCAqKmFubnVhbCBpbmNvbWUqKgoKYGBge3J9CmlzLmZhY3RvcihsZW5kaW5nX3RyYWluaW5nJGludF9yYXRlKQpgYGAKYGBge3J9CmlzLmZhY3RvcihsZW5kaW5nX3RyYWluaW5nJHN1Yl9ncmFkZSkKCmBgYAoKYGBge3J9CmlzLmZhY3RvcihsZW5kaW5nX3RyYWluaW5nJG9wZW5faWxfMjRtKQoKYGBgCgpgYGB7cn0KaXMuZmFjdG9yKGxlbmRpbmdfdHJhaW5pbmckYW5udWFsX2luYykKCmBgYAoKCgpgYGB7cn0KcmZfcGRwIDwtIG1vZGVsX3Byb2ZpbGUoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9IGMoImludF9yYXRlIiwgIm9wZW5faWxfMjRtIiwgImFubnVhbF9pbmMiKSkKCnBsb3QocmZfcGRwLCAKICAgICB2YXJpYWJsZXMgPSBjKCJpbnRfcmF0ZSIsICJvcGVuX2lsXzI0bSIsICJhbm51YWxfaW5jIiksCiAgICAgZ2VvbSA9ICJwcm9maWxlcyIpCmBgYAoKCioqRGlzY3VzcyBob3cgeW91IGNvdWxkIGdvIGFib3V0IGNvbnN0cnVjdGluZyBhIHBhcnRpYWwgZGVwZW5kZW5jZSBwbG90IGZvciBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlKioKCgotLSBTdWJncmFkZSA9IENhdGVnb3JpY2FsIAoKLS0gU3RlcHMgdG8gQ3JlYXRlIFBEUCBmb3Igc3ViZ3JhZGUKCisrKysgb25lIG1ham9yIHN0ZXAgdG8gZG8gYW5kIHRoZW4gY2FuIHVzZSBjb2RlIGFib3ZlOiBjb252ZXJ0IGZyb20gY2F0ZWdvcmljYWwgLyBmYWN0b3IgdG8gbnVtZXJpYyB2YWx1ZSAhISEKCi0tIGZvciBlYWNoIGxldmVsIG9mIHN1YmdyYWRlLCB3b3JraW5nIGZyb20gdG9wIHRvIGJvdHRvbSwgYXNzaWduIGEgbnVtZXJpYyB2YWx1ZSB1c2luZyBhIHNlcmllcyBvZiBpZmVsc2Ugc3RhdGVtZW50cyB3aXRoaW4gbXV0YXRlIGZvciBzdWJncmFkZSB3aXRoaW4gbGVuZGluZyB0cmFpbmluZwoKLS0gbm93LCB3IHN1YmdyYWRlIGJlaW5nIG51bWVyaWMsIGNhbiBmZWVkIHJpZ2h0IGludG8gdGhlIGxpc3Qgb2YgdmFyaWFibGVzIGFib3ZlLi4uCgoKKio1LiBDaG9vc2UgMyBvYnNlcnZhdGlvbnMgYW5kIGRvIHRoZSBmb2xsb3dpbmcgZm9yIGVhY2ggb2JzZXJ2YXRpb246KiogIAoKCioqT0JTRVJWQVRJT04gMioqCgpgYGB7cn0Kb2JzMl9xNSA8LSBsZW5kaW5nX3Rlc3QgJT4lIAogIHNsaWNlKDIpCm9iczJfcTUKYGBgCgoKICAtIENvbnN0cnVjdCBhIGJyZWFrLWRvd24gcGxvdCB1c2luZyB0aGUgZGVmYXVsdCBvcmRlcmluZy4gCiAgCmBgYHtyfQpwcF9yZl8yIDwtIHByZWRpY3RfcGFydHMoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyX3E1LAogICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiYnJlYWtfZG93biIpIAoKcGxvdChwcF9yZl8yKQpgYGAKICAKKipJbnRlcnByZXQgdGhlIHJlc3VsdGluZyBncmFwaC4gV2hpY2ggdmFyaWFibGVzIGNvbnRyaWJ1dGUgbW9zdCB0byBlYWNoIG9ic2VydmF0aW9uJ3MgcHJlZGljdGlvbj8qKgoKRmlyc3Qgd2UgY2FuIHNlZXRoZSBpbml0aWFsIGJhcnMgc3RhcnQgYXQgdGhlIGludGVyY2VwdCBvZiAwLjg0OCB3aGljaCBtYXJrcyB0aGUgdmFsdWUgb2YgdGhlIGF2ZXJhZ2UgcHJlZGljdGVkIGNsYXNzICgxID0gZ29vZCwgMCA9IGJhZCkgd2hlbiBhcHBseWluZyB0aGUgcmYgbW9kZWwgdG8gdGhlIHRyYWluaW5nIGRhdGEuICAKCldlIGNhbiBzZWUgd2UgZW5kIHVwIDAuMTA4IGxvd2VyIGluIG91ciBwcmVkaWN0ZWQgY2xhc3Mgb2YgcGF5aW5nIGJhY2sgYSBsb2FuIHRoYW4gdGhlIGludGVyY2VwdCB0aGFua3MgdG8gc29tZSBoZWF2eSBoaXR0aW5nIG5lZ2F0aXZlIHZhcmlhYmxlcy4gVGhlIHJlc3VsdGluZyAwLjc0IGlzIGEgZmFpcmx5IHN0cm9uZyBnb29kIHByZWRpY3Rpb24uICAKCldlIGNhbiBzZWUgdGhhdCB0aGUgbGFyZ2VzdCBhbmQgbW9zdCBpbmZsdWVudGlhbCB0dWdzIGZyb20gdmFyaWFibGVzIGRvd24gdG93YXJkcyB0aGUgYWN0dWFsIHByZWRpY3RlZCB2YWx1ZSBmcm9tIHRoZSBpbnRlcmNlcHQgY29tZXMgZnJvbToKCgp0b3RhbF9iYWxfaWwgPT0gNTU0NDUgLS0+IC0wLjA0OCAKCm51bV9pbF90bCA9PSAxMCAtLT4gLTAuMDMyCgpvcGVuX2lsXzI0bSA9PSAyIC0tPiAtMC4wMjkKCgpUaGlzIG1lYW5zIHRoYXQgZm9yIGVhY2ggb2YgdGhlc2UgdmFyaWFibGVzLCBpZiB0aGV5IHdlcmUgZml4ZWQgYXQgdGhlIHZhbHVlcyB0aGV5IGFyZSBzZXQgdG8sIHRoZSBjaGFuZ2UgaW4gYXZlcmFnZSBjbGFzcyBwcmVkaWN0aW9uIHdvdWxkIGJlIHRoZXNlIGxhcmdlIG5lZ2F0aXZlIHZhbHVlcy4gIAogIAogIAogIC0gQ29uc3RydWN0IGEgU0hBUCBncmFwaCBhbmQgaW50ZXJwcmV0IGl0LiAKICAKYGBge3J9CnJmX3NoYXBfMiA8LXByZWRpY3RfcGFydHMoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgbmV3X29ic2VydmF0aW9uID0gb2JzMl9xNSwKICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzaGFwIiwKICAgICAgICAgICAgICAgICAgICAgICAgQiA9IDIwIAopCgpwbG90KHJmX3NoYXBfMikKYGBgCgogIAoqKkRvZXMgaXQgdGVsbCBhIHNpbWlsYXIgc3RvcnkgdG8gdGhlIGJyZWFrLWRvd24gcGxvdD8qKgoKV2UgY2FuIHNlZSB0aGF0IFNFVkVSQUwgdmFyaWFibGVzIGhhdmUgYm94cGxvdHMgb2YgdGhlaXIgZWZmZWN0cyBzdHJhZGRsaW5nIHplcm8gc3ByZWFkaW5nIGFjcm9zcyBib3RoIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSB2YWx1ZXMuICAKCgpUaGVzZSB2YXJpYWJsZXMgc2V0IHRvIHRoZWlyIGFwcHJvcHJpYXRlIGNvbnN0YW50cyBpbmNsdWRlOgoKdG90YWxfYmFsX2lsCgppbnRfcmF0ZQoKc3ViX2dyYWRlCgpudW1faWxfdGwKCmVtcF9sZW5ndGgKCnRvdGFsX2lsX2hpZ2hfY3JlZGl0X2xpbWl0CgoKVGhpcyBtZWFucyB0aGF0IHdoZW4gd2UgY2hhbmdlZCB0aGUgb3JkZXIgb2YgY29uc2lkZXJpbmcgdGhlc2UgdmFyaWFibGVzIGFuZCByZS1ydW4gdGhlIGJyZWFrZG93biB0ZXN0IDIwIHRpbWVzLCB3ZSBoYXZlIHRoZSB2YXJpYWJsZXMgaGF2aW5nIG11bHRpcGxlIGluc3RhbmNlcyBvZiBib3RoIG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSBlZmZlY3RzLiAgCgpUaGlzIG5hcnJhdGl2ZSBpcyBpbiBkaXJlY3QgY29uZmxpY3Qgd2l0aCB0aGUgY2xlYXIgY3V0IHBpY3R1cmUgd2Ugd2VyZSBwYWludGVkIGFib3ZlIGFib3V0IHZhcmlhYmxlIGVmZmVjdHMuICAKCiAgCiAgLSBDb25zdHJ1Y3QgYSBMSU1FIGdyYXBoIChmb2xsb3cgbXkgY29kZSBjYXJlZnVsbHkpLiAKICAKICAKYGBge3J9Cm1vZGVsX3R5cGUuZGFsZXhfZXhwbGFpbmVyIDwtIERBTEVYdHJhOjptb2RlbF90eXBlLmRhbGV4X2V4cGxhaW5lcgpwcmVkaWN0X21vZGVsLmRhbGV4X2V4cGxhaW5lciA8LSBEQUxFWHRyYTo6cHJlZGljdF9tb2RlbC5kYWxleF9leHBsYWluZXIKCnNldC5zZWVkKDIpCmxpbWVfcmZfMiA8LSBwcmVkaWN0X3N1cnJvZ2F0ZShleHBsYWluZXIgPSByZl9leHBsYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG9iczJfcTUgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLUNsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9mZWF0dXJlcyA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9wZXJtdXRhdGlvbnMgPSAxMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAibGltZSIpCgpsaW1lX3JmXzIgJT4lIAogIHNlbGVjdChtb2RlbF9yMiwgbW9kZWxfcHJlZGljdGlvbiwgcHJlZGljdGlvbikgJT4lIAogIGRpc3RpbmN0KCkKYGBgCgogIApgYGB7cn0KcGxvdChsaW1lX3JmXzIpICsKICBsYWJzKHggPSAiVmFyaWFibGUiKQpgYGAKCiAgCiAgCioqSG93IGNsb3NlIGlzIGVhY2ggb3JpZ2luYWwgcHJlZGljdGlvbiB0byB0aGUgcHJlZGljdGlvbiBmcm9tIHRoZSBsb2NhbCBtb2RlbD8gSW50ZXJwcmV0IHRoZSByZXN1bHQuIFlvdSBjYW4gYWxzbyB0cnkgdXNpbmcgZmV3ZXIgb3IgbW9yZSB2YXJpYWJsZXMgaW4gdGhlIGxvY2FsIG1vZGVsIHRoYW4gSSB1c2VkIGluIHRoZSBleGFtcGxlLioqCiAgCiAgClByZWRpY3Rpb24gZnJvbSBPcmlnaW5hbCBSRiBNb2RlbDogMC43NAoKUHJlZGljdGlvbiBmcm9tIExvY2FsIE1vZGVsOiAgMC44MiAoKzAuMDgpCgoqKlRoZSBwcmVkaWN0aW9ucyBhcmUgcHJldHR5IGNsb3NlLi4udGhlIExvY2FsIE1vZGVsIGlzICswLjA4Li4uKioKCi0tIFRoZXkgYXJlIGJhc2ljYWxseSB0aGUgc2FtZSBiZWNhdXNlIG1vc3QgdmFyaWFibGVzIGhhdmUgcm91Z2hseSB0aGUgc2FtZSBpbXBhY3QuLi4gcHJvYmFibHkgYmVjYXVzZSB0aGUgdG90YWxfaWxfaGlnaF9jcmVkaXRfbGltaXQgdmFyaWFibGUgYXBwZWFycyB0byBoYXZlIHNsaWdodGx5IGdyZWF0ZXIgaW1wYWN0IGhlcmUgYW5kIHNvbWUgdmFyaWFibGVzIGltcGFjdHMgbWF5IGJlIG9uIHRoZSBoaWdoZXIgZW5kIG9mIHRoZWlyIGJveHBsb3RzLi4uYWxzbyBqdXN0IHNvIGhhcHBlbnMgMyAvIDUgdmFycyBoYXZlIHBvc2l0aXZlIGltcGFjdHMgb2YgYWxsIHNhbWUgc2l6ZS4uLi4gCgoKTG9jYWwgTW9kZWwgUl4yOiAwLjA1IC0tIE5vdCBHb29kCiAgCkJhciBQbG90IFNob3dzIFZhcmlhYmxlIEltcG9ydGFuY2UgRnJvbSBMb2NhbCBNb2RlbDogIAoKLS0gYW5udWFsIGluY29tZSBuZCB0b3RhbCBiYWwgaWwgaGF2ZSBiaWcgbmVnYXRpdmUgZWZmZWN0cwoKLS0gdGhlIHJlc3QgYXBwZWFyIHRvIGhhdmUgZXF1YWxseSBhcyBoaWdoIGltcGFjdCB2YXJzIGJ1dCBwb3NpdGl2ZQogIAogIAogIAoqKk9CU0VSVkFUSU9OIDIwKioKCmBgYHtyfQpvYnMyMCA8LSBsZW5kaW5nX3Rlc3QgJT4lIAogIHNsaWNlKDIwKQpvYnMyMApgYGAKCi0gQ29uc3RydWN0IGEgYnJlYWstZG93biBwbG90IHVzaW5nIHRoZSBkZWZhdWx0IG9yZGVyaW5nLiAKICAKYGBge3J9CnBwX3JmXzIwIDwtIHByZWRpY3RfcGFydHMoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImJyZWFrX2Rvd24iKSAKCnBsb3QocHBfcmZfMjApCmBgYAoKCioqSW50ZXJwcmV0IHRoZSByZXN1bHRpbmcgZ3JhcGguIFdoaWNoIHZhcmlhYmxlcyBjb250cmlidXRlIG1vc3QgdG8gZWFjaCBvYnNlcnZhdGlvbidzIHByZWRpY3Rpb24/KioKCgpGaXJzdCB3ZSBjYW4gc2VlICB0aGUgaW5pdGlhbCBiYXJzIHN0YXJ0IGF0IHRoZSBpbnRlcmNlcHQgb2YgMC44NDggd2hpY2ggbWFya3MgdGhlIHZhbHVlIG9mIHRoZSBhdmVyYWdlIHByZWRpY3RlZCBjbGFzcyAoMSA9IGdvb2QsIDAgPSBiYWQpIHdoZW4gYXBwbHlpbmcgdGhlIHJmIG1vZGVsIHRvIHRoZSB0cmFpbmluZyBkYXRhLiAgCgpXZSBjYW4gc2VlIHdlIGVuZCB1cCAwLjAxOCBsb3dlciBpbiBvdXIgcHJlZGljdGVkIGNsYXNzIG9mIHBheWluZyBiYWNrIGEgbG9hbiB0aGFuIHRoZSBpbnRlcmNlcHQuICBUaGlzIHZlcnkgbGl0dGxlIGNoYW5nZSBpcyBkdWUgdG8gdGhlIGZhY3QgdGhhdCBhIGxvdCBvZiB0aGUgdmFyaWFibGVzJyBlZmZlY3RzIGhhdmUgY2FuY2VsbGluZyBlZmZlY3RzIChmYWlybHkgZXZlbiBzcGxpdCBvZiArIGFuZCAtIGVmZmVjdHMpLiAgVGhlIHJlc3VsdGluZyAwLjgzIGlzIGEgc3Ryb25nIGdvb2QgcHJlZGljdGlvbi4gICAgIAoKV2UgY2FuIHNlZSB0aGF0IHRoZSBsYXJnZXN0IHR1Z3MgY29tZSBmcm9tIHZhcmlhYmxlcyBib3RoICsgYW5kIC0gKGFuZCBJIGd1ZXNzIHRoYXQgbWFrZXMgdGhlbSBtb3N0IGluZmx1ZW50aWFsIGFzIHRoZXkgY2FuY2VsIGVhY2ggb3RoZXIgb3V0IGZvciB0aGUgbW9zdCBwYXJ0KS4uLjoKCgppbnRfcmF0ZSA9PSAxMi45OSAtLT4gLTAuMDQgCgp0b3RhbF9iYWxfaWwgPT0gMjYyNzUgLS0+IDAuMDM3CgppbnFfZmkgPT0gNCAtLT4gLTAuMDI4CgoKVGhpcyBtZWFucyB0aGF0IGZvciBlYWNoIG9mIHRoZXNlIHZhcmlhYmxlcywgaWYgdGhleSB3ZXJlIGZpeGVkIGF0IHRoZSB2YWx1ZXMgdGhleSBhcmUgc2V0IHRvLCB0aGUgY2hhbmdlIGluIGF2ZXJhZ2UgY2xhc3MgcHJlZGljdGlvbiB3b3VsZCBiZSB0aGVpciBvdXRwdXR0ZWQgdmFscy4gIAoKVGhpcyB3YXMgYSB3ZWlyZCBvbmUgdG8gZXZhbCBjb250cmlidXRpb246IDEpIGRvIHRoZSBiaWdnZXIgdHVncyBzdGlsbCBjb3VudCBhcyBtb3N0IHNpZ25pZmljYW50IGJlY2F1c2UgdGhleSBhcmUgY2FuY2VsbGluZyBlYWNob3RoZXIgb3V0IG9yIDIpIGRvIHRoZSBzbWFsbCBuZWdhdGl2ZSB0dWdzIGNvdW50IG1vcmUgZXZlbiB0aG91Z2ggdGhleSB3b3VsZG4ndCBtYXR0ZXIgaWYgdGhlIGJpZyB0dWdzIGRpZG4ndCBjYW5jZWwgb3V0Pz8/PwoKICAKICAtIENvbnN0cnVjdCBhIFNIQVAgZ3JhcGggYW5kIGludGVycHJldCBpdC4gCiAgCmBgYHtyfQpyZl9zaGFwXzIwIDwtcHJlZGljdF9wYXJ0cyhleHBsYWluZXIgPSByZl9leHBsYWluLAogICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyMCwKICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzaGFwIiwKICAgICAgICAgICAgICAgICAgICAgICAgQiA9IDIwIAopCgpwbG90KHJmX3NoYXBfMjApCmBgYAogIAogIAogIAogICoqRG9lcyBpdCB0ZWxsIGEgc2ltaWxhciBzdG9yeSB0byB0aGUgYnJlYWstZG93biBwbG90PyoqCgpXZSBjYW4gc2VlIHRoYXQgU0VWRVJBTCB2YXJpYWJsZXMgaGF2ZSBib3hwbG90cyBvZiB0aGVpciBlZmZlY3RzIHN0cmFkZGxpbmcgemVybyBzcHJlYWRpbmcgYWNyb3NzIGJvdGggcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHZhbHVlcy4gIAoKClRoZXNlIHZhcmlhYmxlcyBzZXQgdG8gdGhlaXIgYXBwcm9wcmlhdGUgY29uc3RhbnRzIGluY2x1ZGU6Cgp0b3RhbF9iYWxfaWwKCnN1Yl9ncmFkZQoKYW5udWFsX2luYwoKaW50X3JhdGUKCmFkZHJfc3RhdGUKCmFsbF91dGlsCgppbnFfbGFzdF82bXRocwoKClRoaXMgbWVhbnMgdGhhdCB3aGVuIHdlIGNoYW5nZWQgdGhlIG9yZGVyIG9mIGNvbnNpZGVyaW5nIHRoZXNlIHZhcmlhYmxlcyBhbmQgcmUtcnVuIHRoZSBicmVha2Rvd24gdGVzdCAyMCB0aW1lcywgd2UgaGF2ZSBhbG1vc3QgZXZlcnkgc2luZ2xlIHZhcmlhYmxlIGhhdmluZyBtdWx0aXBsZSBpbnN0YW5jZXMgb2YgYm90aCBuZWdhdGl2ZSBhbmQgcG9zaXRpdmUgZWZmZWN0cy4gIAoKVGhpcyBuYXJyYXRpdmUgaXMgaW4gZGlyZWN0IGNvbmZsaWN0IHdpdGggdGhlIGNsZWFyIGN1dCBwaWN0dXJlIHdlIHdlcmUgcGFpbnRlZCBhYm92ZSBhcyB0aGUgdmFycyBlZmZlY3RzIGFyZSBub3QgYXMgY2xlYXIgYXMgd2UgdGhvdWdodCBhYm92ZS4gIAoKICAKICAtIENvbnN0cnVjdCBhIExJTUUgZ3JhcGggKGZvbGxvdyBteSBjb2RlIGNhcmVmdWxseSkuIAogIAoKYGBge3J9Cm1vZGVsX3R5cGUuZGFsZXhfZXhwbGFpbmVyIDwtIERBTEVYdHJhOjptb2RlbF90eXBlLmRhbGV4X2V4cGxhaW5lcgpwcmVkaWN0X21vZGVsLmRhbGV4X2V4cGxhaW5lciA8LSBEQUxFWHRyYTo6cHJlZGljdF9tb2RlbC5kYWxleF9leHBsYWluZXIKCnNldC5zZWVkKDIpCmxpbWVfcmZfMjAgPC0gcHJlZGljdF9zdXJyb2dhdGUoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyMCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtQ2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX2ZlYXR1cmVzID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3Blcm11dGF0aW9ucyA9IDEwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJsaW1lIikKCmxpbWVfcmZfMjAgJT4lIAogIHNlbGVjdChtb2RlbF9yMiwgbW9kZWxfcHJlZGljdGlvbiwgcHJlZGljdGlvbikgJT4lIAogIGRpc3RpbmN0KCkKYGBgCgogIApgYGB7cn0KcGxvdChsaW1lX3JmXzIwKSArCiAgbGFicyh4ID0gIlZhcmlhYmxlIikKYGBgCiAgCiAgCiAgCioqSG93IGNsb3NlIGlzIGVhY2ggb3JpZ2luYWwgcHJlZGljdGlvbiB0byB0aGUgcHJlZGljdGlvbiBmcm9tIHRoZSBsb2NhbCBtb2RlbD8gSW50ZXJwcmV0IHRoZSByZXN1bHQuIFlvdSBjYW4gYWxzbyB0cnkgdXNpbmcgZmV3ZXIgb3IgbW9yZSB2YXJpYWJsZXMgaW4gdGhlIGxvY2FsIG1vZGVsIHRoYW4gSSB1c2VkIGluIHRoZSBleGFtcGxlLioqCiAgCiAgClByZWRpY3Rpb24gZnJvbSBPcmlnaW5hbCBSRiBNb2RlbDogMC44MwoKUHJlZGljdGlvbiBmcm9tIExvY2FsIE1vZGVsOiAgMC44ICgtMC4wMykKCioqVGhlIHByZWRpY3Rpb25zIGFyZSBmYWlybHkgY2xvc2UuLi50aGUgTG9jYWwgTW9kZWwgaXMgLTAuMDMuLi4qKgoKLS0gVGhpcyBsaWtlbHkgaXMgYmVjYXVzZSBvZiB0aGUgbWFzc2l2ZSBuZWdhdGl2ZSBpbXBhY3Qgb2YgcmV2b2xfdXRpbCBhbmQgYW5udWFsX2luYyB3aGljaCBwdXNoZWQgdGhlIHByZWQgc2xpZ2h0bHkgYmVsb3cgYXMgdGhleSBjb3VudGVyZWQgdGhlIHBvcyBpbXBhY3QgZnJvbSBvdGhlcnMuICBBbm51YWwgSW5jb21lJ3MgZWZmZWN0cyBhcHBlYXIgdG8gYmUgbXVjaCBtb3JlIG5lZ2F0aXZlIGhlcmUgdGhhbiBhYm92ZS4uLiAgCgpMb2NhbCBNb2RlbCBSXjI6IDAuMDcgLS0gVGVycmlibGUKICAKQmFyIFBsb3QgU2hvd3MgVmFyaWFibGUgSW1wb3J0YW5jZSBGcm9tIExvY2FsIE1vZGVsOiAgCgotLSByZXZvbF91dGlsIGhhcyBhIG1hc3NpdmUgbmVnYXRpdmUgaW1wYWN0CgotLSBhbm51YWwgaW5jb21lIGhhcyBlcXVhbGx5IG1hc3NpdmUgbmVnYXRpdmUgaW1wYWN0CgotLSBpbnEgbGFzdCA2IG10aHMgYW5kIHRvdGFsIGlsIGhpZ2ggY3JlZGl0IGhhdmUgbW9kZXJhdGVseSBsYXJnZSBwb3NpdGl2ZSBpbXBhY3RzCiAgCgoKKipPQlNFUlZBVElPTiAyMDAqKgoKYGBge3J9Cm9iczIwMCA8LSBsZW5kaW5nX3Rlc3QgJT4lIAogIHNsaWNlKDIwMCkKb2JzMjAwCmBgYAoKLSBDb25zdHJ1Y3QgYSBicmVhay1kb3duIHBsb3QgdXNpbmcgdGhlIGRlZmF1bHQgb3JkZXJpbmcuIAogIApgYGB7cn0KcHBfcmZfMjAwIDwtIHByZWRpY3RfcGFydHMoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJicmVha19kb3duIikgCgpwbG90KHBwX3JmXzIwMCkKYGBgCiAgCiAgCioqSW50ZXJwcmV0IHRoZSByZXN1bHRpbmcgZ3JhcGguIFdoaWNoIHZhcmlhYmxlcyBjb250cmlidXRlIG1vc3QgdG8gZWFjaCBvYnNlcnZhdGlvbidzIHByZWRpY3Rpb24/KioKCgpGaXJzdCB3ZSBjYW4gc2VlICB0aGUgaW5pdGlhbCBiYXJzIHN0YXJ0IGF0IHRoZSBpbnRlcmNlcHQgb2YgMC44NDggd2hpY2ggbWFya3MgdGhlIHZhbHVlIG9mIHRoZSBhdmVyYWdlIHByZWRpY3RlZCBjbGFzcyAoMSA9IGdvb2QsIDAgPSBiYWQpIHdoZW4gYXBwbHlpbmcgdGhlIHJmIG1vZGVsIHRvIHRoZSB0cmFpbmluZyBkYXRhLiAgICAKCldlIGNhbiBzZWUgd2UgZW5kIHVwIDAuMTMyIEhJR0hFUiBpbiBvdXIgcHJlZGljdGVkIGNsYXNzIG9mIHBheWluZyBiYWNrIGEgbG9hbiB0aGFuIHRoZSBpbnRlcmNlcHQgdGhhbmtzIHRvIHNvbWUgYmlnIHBvc2l0aXZlIGp1bXBzIGZyb20gaW5mbHVlbnRpYWwgdmFyaWFibGVzLiAgVGhlIHJlc3VsdGluZyAwLjk4IGlzIGFuIGV4dHJlbWVseSBzdHJvbmcgZ29vZCBwcmVkaWN0aW9uLiAgCgpXZSBjYW4gc2VlIHRoYXQgdGhlIGxhcmdlc3QgcG9zaXRpdmUganVtcHMgZnJvbSB2YXJpYWJsZXMgY29tZSBmcm9tOgoKCmludF9yYXRlID09IDEzLjQ0IC0tPiAwLjAyOSAKCnN1Yl9ncmFkZSA9PSAxMyAtLT4gMC4wMjkKCnRvdGFsX2JhbF9pbCA9PSAwIC0tPiAwLjAyOAoKYW5kIGFsc28gaXQgaGVscHMgdGhhdCBhbGwgYnV0IDMgaW4gdGhpcyBjYXNlIGhhdmUgcG9zaXRpdmUgaW1wYWN0cy4uLgoKClRoaXMgbWVhbnMgdGhhdCBmb3IgZWFjaCBvZiB0aGVzZSB2YXJpYWJsZXMsIGlmIHRoZXkgd2VyZSBmaXhlZCBhdCB0aGUgdmFsdWVzIHRoZXkgYXJlIHNldCB0bywgdGhlIGNoYW5nZSBpbiBhdmVyYWdlIGNsYXNzIHByZWRpY3Rpb24gd291bGQgYmUgdGhlc2UgbGFyZ2UgcG9zaXRpdmUgdmFsdWVzIHdoaWNoIHB1bGxlZCBvdXIgcHJlZGljdGlvbiBmb3IgdGhpcyBvYnNlcnZhdGlvbiB1cCB2ZXJ5IGhpZ2guICAKCiAgCiAgLSBDb25zdHJ1Y3QgYSBTSEFQIGdyYXBoIGFuZCBpbnRlcnByZXQgaXQuIAogIApgYGB7cn0KcmZfc2hhcF8yMDAgPC1wcmVkaWN0X3BhcnRzKGV4cGxhaW5lciA9IHJmX2V4cGxhaW4sCiAgICAgICAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG9iczIwMCwKICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzaGFwIiwKICAgICAgICAgICAgICAgICAgICAgICAgQiA9IDIwIAopCgpwbG90KHJmX3NoYXBfMjAwKQpgYGAKICAKICAKICAKKipEb2VzIGl0IHRlbGwgYSBzaW1pbGFyIHN0b3J5IHRvIHRoZSBicmVhay1kb3duIHBsb3Q/KioKCgpXZSBjYW4gc2VlIHRoYXQgU0VWRVJBTCB2YXJpYWJsZXMgaGF2ZSBib3hwbG90cyBvZiB0aGVpciBlZmZlY3RzIHN0cmFkZGxpbmcgemVybyBzcHJlYWRpbmcgYWNyb3NzIGJvdGggcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHZhbHVlcy4gIAoKClRoZXNlIHZhcmlhYmxlcyBzZXQgdG8gdGhlaXIgYXBwcm9wcmlhdGUgY29uc3RhbnRzIGluY2x1ZGU6Cgp0b3RhbF9iYWxfaWwKCnN1Yl9ncmFkZQoKaW50X3JhdGUKCnRvdGFsX2lsX2hpZ2hfY3JlZGl0X2xpbWl0Cgphbm51YWxfaW5jCgpudW1faWxfdGlsCgoKVGhpcyBtZWFucyB0aGF0IHdoZW4gd2UgY2hhbmdlZCB0aGUgb3JkZXIgb2YgY29uc2lkZXJpbmcgdGhlc2UgdmFyaWFibGVzIGFuZCByZS1ydW4gdGhlIGJyZWFrZG93biB0ZXN0IDIwIHRpbWVzLCB3ZSBoYXZlIHRoZXNlIHZhcmlhYmxlcyBoYXZpbmcgbXVsdGlwbGUgaW5zdGFuY2VzIG9mIGJvdGggbmVnYXRpdmUgYW5kIHBvc2l0aXZlIGVmZmVjdHMuICAKClRoaXMgbmFycmF0aXZlIGlzIGluIGRpcmVjdCBjb25mbGljdCB3aXRoIHRoZSBjbGVhciBjdXQgcGljdHVyZSB3ZSB3ZXJlIHBhaW50ZWQgYWJvdmUgYXMgd2Ugc2VlIHRoZSBpbXBhY3RzIG9mIHZhcnMgYXJlIG5vdCBhcyBvYnZpb3VzLiAgCiAgCiAgCiAgCiAgLSBDb25zdHJ1Y3QgYSBMSU1FIGdyYXBoIChmb2xsb3cgbXkgY29kZSBjYXJlZnVsbHkpLiAKICAKCmBgYHtyfQptb2RlbF90eXBlLmRhbGV4X2V4cGxhaW5lciA8LSBEQUxFWHRyYTo6bW9kZWxfdHlwZS5kYWxleF9leHBsYWluZXIKcHJlZGljdF9tb2RlbC5kYWxleF9leHBsYWluZXIgPC0gREFMRVh0cmE6OnByZWRpY3RfbW9kZWwuZGFsZXhfZXhwbGFpbmVyCgpzZXQuc2VlZCgyKQpsaW1lX3JmXzIwMCA8LSBwcmVkaWN0X3N1cnJvZ2F0ZShleHBsYWluZXIgPSByZl9leHBsYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG9iczIwMCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtQ2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX2ZlYXR1cmVzID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3Blcm11dGF0aW9ucyA9IDEwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJsaW1lIikKCmxpbWVfcmZfMjAwICU+JSAKICBzZWxlY3QobW9kZWxfcjIsIG1vZGVsX3ByZWRpY3Rpb24sIHByZWRpY3Rpb24pICU+JSAKICBkaXN0aW5jdCgpCmBgYAoKICAKYGBge3J9CnBsb3QobGltZV9yZl8yMDApICsKICBsYWJzKHggPSAiVmFyaWFibGUiKQpgYGAKICAKICAKICAKICAqKkhvdyBjbG9zZSBpcyBlYWNoIG9yaWdpbmFsIHByZWRpY3Rpb24gdG8gdGhlIHByZWRpY3Rpb24gZnJvbSB0aGUgbG9jYWwgbW9kZWw/IEludGVycHJldCB0aGUgcmVzdWx0LiBZb3UgY2FuIGFsc28gdHJ5IHVzaW5nIGZld2VyIG9yIG1vcmUgdmFyaWFibGVzIGluIHRoZSBsb2NhbCBtb2RlbCB0aGFuIEkgdXNlZCBpbiB0aGUgZXhhbXBsZS4qKgogIAoKUHJlZGljdGlvbiBmcm9tIE9yaWdpbmFsIFJGIE1vZGVsOiAwLjk4CgpQcmVkaWN0aW9uIGZyb20gTG9jYWwgTW9kZWw6ICAwLjgxICgtMC4xNykKCioqVGhlIHByZWRpY3Rpb25zIGFyZSBub3QgY2xvc2UuLi50aGUgTG9jYWwgTW9kZWwgaXMgLTAuMTcuLi4qKgoKLS0gVGhpcyBsaWtlbHkgaXMgYmVjYXVzZSBoZXJlIHdlIGhhdmUgbWFzc2l2ZSBuZWdhdGl2ZSBpbXBhY3RzIHNlZW4gZnJvbSByZXZvbF91dGlsIGFuZCBhbm51YWxfaW5jb21lLCBtdWNoIGhpZ2hlciB0aGFuIGRpc3BsYXllZCBhYm92ZSwgd2hpY2ggYWNjb3VudCBmb3IgdGhlIG1hc3NpdmUgZHJvcCBpbiBwcmVkaWN0ZWQgY2xhc3Mgc2NvcmUuICAKCgpMb2NhbCBNb2RlbCBSXjI6IDAuMDYgLS0gQXdmdWwKICAKQmFyIFBsb3QgU2hvd3MgVmFyaWFibGUgSW1wb3J0YW5jZSBGcm9tIExvY2FsIE1vZGVsOiAgCgotLSByZXZvbF91dGlsLCBhbm51YWwgaW5jb21lIGhhdmUgbWFzc2l2ZWx5IGxhcmdlIG5lZ2F0aXZlIGltcGFjdHMgdGhhdCBvdXRwYWNlIHRoZSBwb3NpdGl2ZSBpbXBhY3RzIG9mIGlucSBsYXN0IDZtdGhzLCBpbnFfZmksIGFuZCB0b3RhbF9iYWxfaWwuICAKICAKICAKKio2LiBEZXNjcmliZSBob3cgeW91IHdvdWxkIHVzZSB0aGUgaW50ZXJwcmV0YWJsZSBtYWNoaW5lIGxlYXJuaW5nIHRvb2xzIHdlJ3ZlIGxlYXJuZWQgKGJvdGggbG9jYWwgYW5kIGdsb2JhbCkgaW4gZnV0dXJlIG1hY2hpbmUgbGVhcm5pbmcgcHJvamVjdHM/IEhvdyBkb2VzIGVhY2ggb2YgdGhlbSBoZWxwIHlvdT8qKgoKCkkgY291bGQgZmluZCBncmVhdCB1c2UgaW4gdXNpbmcgdGhlIGJveHBsb3QgYW5kIGhpc3RvZ3JhbSBvZiByZXNpZHVhbHMgZm9yIGEgbW9kZWwgZnJvbSBnbG9iYWwgaW50ZXJwcmV0YWJsZSBNTCBhbmQgZnJvbSBsb2NhbCBpbnRlcnByZXRhYmxlIE1MIEkgd291bGQgZGVmaW5pdGVseSBoYXZlIGdyZWF0IHZhbHVlIGZyb20gdXNpbmcgYnJlYWtkb3duIHByb2ZpbGVzIGFuZCBzaGFwbGV5IHBsb3RzLiAKCioqRm9yIEdsb2JhbDogIFZpc3VhbGl6aW5nIFJlc2lkdWFscyoqIAoKVGhpcyBwYXN0IFN1bW1lciwgSSBoYWQgYW4gaW50ZXJuc2hpcCB3aXRoIGEgRmludGVjaCBjb21wYW55IGNhbGxlZCBBdmlkWENoYW5nZSwgYSBDaGFybG90dGUsIE5DIGJhc2VkIEFjY291bnRzIFBheWFibGUgQXV0b21hdGlvbiBjb21wYW55LiAgSSB1c2VkIHNxbCB0byBwdWxsIGRhdGEgZnJvbSBkYXRhYmFzZXMgYW5kIG1hY2hpbmUgbGVhcm5pbmcgc2tpbGxzIHRvIHVzZSBuZXVyYWwgbmV0d29ya3MgYW5kIGRlZXAgbGVhcm5pbmcuICBXZSB3YW50ZWQgdG8gbW9yZSBhY2N1cmF0ZWx5IHByZWRpY3RlZCB3aGljaCBvZiBvdXIgY2xpZW50cyBhY2NlcHRlZCBWaXJ0dWFsIENyZWRpdCBDYXJkIGFuZCB3aGljaCBkaWRuJ3QgYXMgdGhpcyB3YXMgYSBiaWcgcHJvYmxlbS4gIEF2aWQgbWFrZXMgbW9zdCBvZiB0aGVpciBtb25leSBmcm9tIGZlZXMgb24gVkNDIGNhcmRzIGJlaW5nIHVzZWQgdG8gcGF5IG9mZiBpbnZvaWNlcyBhcyBvcHBvc2VkIHRvIGp1c3QgZGlyZWN0IGRlcG9zaXQgKHRoaXMgaXMgbW9yZSBpZGVhbCBmb3IgbW9zdCBhcyB0aGV5IGdldCB0aGUgbW9uZXkgbXVjaCBxdWlja2VyIGRlc3BpdGUgYSB2ZXJ5IHNtYWxsIGZlZSkuICBJZiBhYmxlIHRvIGJldHRlciBpZGVudGlmeSB3aG8gc2ltcGx5IGNhbid0IHRha2UgVkNDLCBpdCB3b3VsZCBzYXZlIGEgbG90IG9mIHRpbWUgYW5kIG1vbmV5IGJldHRlciB1c2VkIG9uIG90aGVyIGNsaWVudHMuCgpXaGVuIHdlIGZpbmFsaXplZCB0aGUgbW9kZSwgSSB3b3VsZCBoYXZlIGxvdmVkIHRvIHNlZSBob3cgd2VsbCBpdCBwcmVkaWN0ZWQgdGhyb3VnaCBzZWVpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBpdHMgcmVzaWR1YWxzLiAgTXkgbW9kZWwgYWxzbyB3YXMgYSBjbGFzcyBwcmVkaWN0aW9uIGxpa2UgdyB0aGUgbGVuZGluZyBkYXRhLCBzcGVjaWZpY2FsbHkgdXNpbmcgdGhlIHNwZWxsaW5nIGFuZCBtYWtlIHVwIG9mIHRoZSBjb21wYW55IG5hbWUgdG8gcHJlZGljdCBpZiB0aGV5IHRvb2sgVkNDICh0aGUgaHVuY2ggd2FzIHRoYXQgY29tcGFuaWVzIHRoYXQgYXBwZWFyZWQgdG8gYmUgcGVvcGxlIGRpZCBub3QgYWNjZXB0IFZDQyBhbmQgdGhlIG1vZGVsIHVzZWQgYSBzZXJpZXMgb2YgZGljdGlvbmFyaWVzIGZvciBsZXR0ZXJzIGFuZCBuYW1lcyBmcm9tIGNlbnN1cyBkYXRhIHRvIGRpc3Rpbmd1aXNoIG5hbWVzKS4gIAoKSWYgSSBjb3VsZCBhbmFseXplIHRoZSByZXNpZHVhbHMgYSBiaXQgbW9yZSB3aXRoIHRoaW5ncyBsaWtlIHRoZSBib3hwbG90IGFuZCBoaXN0b2dyYW0gbGlrZSB3ZSBkaWQgYWJvdmUsIEkgd291bGQgaGF2ZSBiZWVuIGFibGUgdG8gc2VlIGEgYml0IGJldHRlciBob3cgd2VsbCB3ZSBwcmVkaWN0ZWQgY29ycmVjdGx5LiAgU3BlY2lhbGx5LCBJIHdvdWxkIGhhdmUgYmVlbiBhYmxlIHRvIHNlZSBpZiBpdCB3YXMgYSBnb29kIG1vZGVsIGJleW9uZCBzaW1wbHkgbG9va2luZyBhdCBvdXRwdXQgbWV0cmljcyBsaWtlIEkgZGlkLiAgSWYgSSBjb3VsZCBoYXZlIHZpc3VhbGl6ZWQgdGhlIHJlc2lkdWFscyBhbmQgc2VlbiBob3cgaXQgdGVuZGVkIHRvIHByZWRpY3QgdGhlIG1ham9yaXR5IGNsYXNzIG1vcmUgdGhhbiBtb3N0IG90aGVyIHRoaW5ncywgdGhhdCB3b3VsZCBoYXZlIGJlZW4gdmVyeSBlYXN5IHRvIHNlZSBhbmQgY29vbCB0byBoZWxwIHNob3cgdGhlIHJlc3VsdHMgYXMgb3Bwb3NlZCB0byBzaW1wbHkgdGVsbGluZyBwZW9wbGUgdGhlIHJlc3VsdHMgd2l0aCBtZXRyaWNzIGZpbGxlZCB3aXRoIGphcmdvbi4gICAKCioqRm9yIExvY2FsOiAgQnJlYWtkb3duIFByb2ZpbGVzIGFuZCBTaGFwbGV5KioKCkFsc28sIGluIG15IGludGVybnNoaXAgSSBhZGRlZCBvbiB0byBhIFZDQyBtb2RlbCBhIHByb3BlbnNpdHkgc2NvcmUgdmFsdWUgd2hpY2ggcHJlZGljdGVkIGEgcHJvYmFiaWxpdHkgYXQgd2hpY2ggYSBjb21wYW55ICh3ZSB0aG91Z2h0KSB3b3VsZCBhY2NlcHQgVkNDLiAgSXQgdG9vayBpbiBtYW55IHZhcmlhYmxlcyBpbmNsdWRpbmcgcGFzdCBpbnZvaWNlcywgc2l6ZSBvZiB0aGUgaW52b2ljZSwgc2l6ZSBvZiB0aGUgY29tcGFueSwgaWYgdGhleSBoYXZlIHRha2VuIFZDQyBpbiB0aGUgcGFzdCBiZWZvcmUsIGxvY2F0aW9uLi4uIAoKSSB3b3VsZCBoYXZlIGxvdmVkIHRvIHNlZSBmb3Igc3BlY2lmaWMgY29tcGFuaWVzIHdoaWNoIHZhcmlhYmxlcyB3ZXJlIG1vcmUgc2lnbmlmaWNhbnQgaW4gZHJpdmluZyB0aGUgcHJvcGVuc2l0eSBzY29yZXMgdG8gd2hlcmUgdGhleSB3ZW50LiAgT2J2aW91c2x5IFBhbmVyYSB0YWtlcyBWQ0MgYmVjYXVzZSB0aGV5IHRha2UgY3JlZGl0IGNhcmQgYnV0IHdoYXQgdmFyaWFibGUgc3BlY2lmaWNhbGx5IHdhcyBob25pbmcgaW4gb24gdGhpcz8KClNlZWluZyB0aGUgbGl0dGxlIHR1ZyBvZiB3YXIgYmV0d2VlbiB2YXJpYWJsZXMgYW5kIGhvdyB0aGV5IGVmZmVjdGVkIHRoZSBvdXRjb21lIHdvdWQgaGF2ZSBiZWVuIHJlYWxseSBjb29sLiAgQWRkaXRpb25hbGx5LCBJIHdvdWxkIGhhdmUgbGlrZWQgdG8gc2VlIHRoZSBwZXJtYW5lbmNlIG9mIHRoZXNlIHZhcmlhYmxlcyBlZmZlY3RzIGJleW9uZCBqdXN0IG9uZSBvYnNlcnZhdGlvbi4gSWYgSSBjb3VsZCBoYXZlIGFsc28gdXNlZCBhIHNoYXAgcGxvdCwgSSBjb3VsZCBoYXZlIHNlZW4gaG93IGZpcm0gdGhlc2UgdmFyaWFibGUgaW1wYWN0cyB3ZXJlIGFzIHdlbGwgYW5kIHNlZW4gaG93IGl0IGNoYW5nZWQgZnJvbSBsYXJnZSB0byBzbWFsbCBjb21wYW55IG9jY3VycmVuY2VzLiAgIAoKCioqNy4gU2F2ZSB0aGlzIGZpbmFsIG1vZGVsIHVzaW5nIHRoZSBgd3JpdGVfcmRzKClgIGZ1bmN0aW9uIC0gc2VlIHRoZSBzZWN0aW9uIG9mIHRoZSBgdGlkeW1vZGVsc2AgaW50cm8gZm9yIGEgc2ltaWxhciBleGFtcGxlLCBidXQgd2UncmUgdXNpbmcgYHdyaXRlX3JkcygpYCBpbnN0ZWFkIG9mIGBzYXZlUkRTKClgLiBXZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBtb2RlbCBpbiB0aGUgbmV4dCBwYXJ0LiBZb3UnbGwgd2FudCB0byBzYXZlIGl0IGluIHRoZSBmb2xkZXIgd2hlcmUgeW91IGNyZWF0ZSB5b3VyIHNoaW55IGFwcC4gUnVuIHRoZSBjb2RlLCBhbmQgdGhlbiBhZGQgYGV2YWw9RkFMU0VgIHRvIHRoZSBjb2RlIGNodW5rIG9wdGlvbnMgKG5leHQgdG8gdGhlIHIgaW5zaWRlIHRoZSBjdXJseSBicmFja2V0cykgc28gaXQgZG9lc24ndCByZXJ1biB0aGlzIGVhY2ggdGltZSB5b3Uga25pdC4qKiAKCmBgYHtyLCBldmFsPUZBTFNFfQojICMgZmluYWxpemUgbW9kZWwKIyByZl9maW5hbCA8LSByZl93b3JrZmxvdyAlPiUgCiMgICBmaW5hbGl6ZV93b3JrZmxvdyhiZXN0X2FjY3VyYWN5KSAlPiUgCiMgICBmaXQoZGF0YSA9IGxlbmRpbmdfdHJhaW5pbmcpCgoKd3JpdGVfcmRzKHJmX2ZpbmFsLCAicmZfZmluYWwucmRzIikKCnJmX2ZpbmFsX3JlYWQgPC0gcmVhZFJEUygicmZfZmluYWwucmRzIikKCgpgYGAKCgoKCiMjIFNoaW55IGFwcCAtLSBET05FCgpNeSBBcHA6IGh0dHBzOi8vYWpwMjgxMTcuc2hpbnlhcHBzLmlvL0xlbmRpbmdfQ2x1Yi8KClJlcG8gZm9yIEFwcDogaHR0cHM6Ly9naXRodWIuY29tL2FwYWxtYTEyNy9sZW5kaW5nX3NtYWxsCgpXZWJzaXRlIFBvc3Q6IGh0dHBzOi8vYW50aG9ueXBhbG1hLm5ldGxpZnkuYXBwL3Bvc3RzL3NoaW55YXBwcy8gKH5+IHNjcm9sbCBkb3duIHRvIGxlbmRpbmcgZGF0YSBzaGlueSBhcHAgfn4pCgoKIyMgRGF0YSBFdGhpY3M6IERhdGEgdmlzdWFsaXphdGlvbiBwcmluY2lwbGVzIC0tIERPTkUKCldlcmUgdGhlcmUgYW55IHByaW5jaXBsZXMgbWVudGlvbmVkIHRoYXQgeW91IGhhZG4ndCBoZWFyZCBvZiBiZWZvcmU/IAoKKipJIHdhcyB1bmF3YXJlIHRoYXQgdGhlIHkgYXhpcyBvbiBsaW5lIGdyYXBocyBzaG91bGRuJ3QgaW5jbHVkZSAwIGFuZCB0aGF0IHRoZSBncmFwaCBzaG91bGQgYmUgbXVjaCBtb3JlIGhvbmVkIGluIG9uIHRoZSByZWdpb24gb2YgaW50ZXJlc3QgdGhhbiB6b29tZWQgb3V0LiAgSSBhY3R1YWxseSBoYXZlIG5ldmVyIGRvbmUgdGhpcyB0byBiZSBob25lc3QganVzdCBiZWNhdXNlIEkgdGhvdWdodCBpdCB3YXMgZ29vZCBldGlxdWV0dGUgdG8gYWx3YXlzIHN0YXJ0IGF0IDAuLi4qKgoKV2hhdCBncmFwaCBzdG9vZCBvdXQgZm9yIHlvdSBhcyAidGhlIHdvcnN0Ij8gCgoqKlRoZSB3b3JzdCBoYXMgdG8gYmUgdGhlIGJ1YmJsZSBjaGFydCB3aXRoIHRoZSBwcm9mZXNzb3IgaW4gZnJvbnQuICBXaGlsZSBpdCBkb2VzIGhhdmUgYSBsb3Qgb2YgZGF0YSBhbmQgY29vbCB0aGluZ3MgZ29pbmcgb24sIGl0IGlzIGEgbWVzcy4gIFdpdGggbm8gYXhlcyBhbmQgcmFuZG9tIHNpemUgZGlmZmVyZW5jZXMsIHRoZXJlIGlzIHNvIG11Y2ggZ29pbmcgb24gdGhlIG9ubHkgdGhpbmcgeW91IGNhbiBzZWUgaXMgcmVsYXRpdmUgZGlmZmVyZW5jZS4gIEFkZGl0aW9uYWxseSwgdGhlcmUgaXMgYSBsb3Qgb2YgYmxvYmJpbmcgb2YgcG9pbnRzIG9uIHRvcCBvZiBlYWNoIG90aGVyIG1ha2luZyBldmVuIHJlbGF0aXZlIGRpZmZlcmVuY2UgZGlmZmljdWx0IHRvIHNlZS4qKgoKRGlkIGFueSBvZiB0aGUgZ3JhcGhzIGZvb2wgeW91PyAKCioqVGhlIGd1biBkZWF0aHMgaW4gRkwgZ3JhcGggd2FzIHZlcnl5eXl5IGNvbmZ1c2luZyBiZWNhdXNlIG9mIHRoZSB1cHNpZGUgZG93biBmaWxsaW5nIG9mIGl0LiAgQXQgZmlyc3QgSSB3YXMgbGlrZSB3b3cgc3VwZXIgd2VpcmQgc3RhbmQgeW91ciBncm91bmQgREVDUkVBU0VEIGd1biBkZWF0aHMgYnV0IHRoYXQgd2FzIHNpbXBseSBiZWNhdXNlIHRoZSBjcmVhdG9yIG9mIHRoZSBncmFwaCB3YW50ZWQgeW91IHRvIHNlZSBxdWl0ZSB0aGUgb3Bwb3NpdGUgb2YgcmVhbGl0eS4qKgoKSG93IGRvZXMgcHJhY3RpY2luZyBnb29kIGRhdGEgdmlzdWFsaXphdGlvbiBwcmluY2lwbGVzIGZpdCBpbiB3aXRoIGRhdGEgZXRoaWNzPwoKKipUaGUgZmxvcmlkYSBndW4gZGVhdGhzIG9uZSB3YXMgYSByZWFsbHkgZ29vZCBleGFtcGxlIG9mIGhvdyBiYWQgZGF0YSBwcmFjdGljZXMsIG5vIG1hdHRlciB0aGUgaW50ZW50LCBjYW4gYWN0dWFsbHkgbWFuaXB1bGF0ZSB0aGUgdHJ1dGggYW5kIGxpZSB0byB0aGUgdmlld2VyLiAgU3VjaCBmYWtlIG5ld3Mgd291bGQgYmUgZGFtYWdpbmcgdG8gYSBuYXJyYXRpdmUgaW4gYW4gZWxlY3Rpb24gZXNwZWNpYWxseSBiZWNhdXNlIG9mIGhvdyBjb250cmFyeSB0byByZWFsaXR5IGl0IGlzLiAgSWYgc29tZW9uZSB3ZXJlIHRvIGRvIHRoaXMgZHVyaW5nIGVsZWN0aW9uIHRpbWUgd2l0aCBpbXBvcnRhbnQgc3RhdHMgYWJvdXQgYWJvcnRpb24gYW5kIGltbWlncmF0aW9uLCBpdCB3b3VsZCBiZSBjcnVjaWFsIHRvIGNoYW5naW5nIG1pbmRzIGFuZCB2b3Rlcy4qKgo=